package com.basker.pisces.utils;

import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;

import org.springframework.util.Assert;

/**
 * 日期相关的工具类
 *
 * @author hangwen
 */
public class DateUtils {

    /**
     * yyyy-MM-dd HH:mm:ss
     */
    public static final String DEFAULT_DATE_TIME = "yyyy-MM-dd HH:mm:ss";

    /**
     * yyyy-MM-dd
     */
    public static final String DEFAULT_DATE = "yyyy-MM-dd";

    /**
     * HH:mm:ss
     */
    public static final String DEFAULT_TIME = "HH:mm:ss";

    /**
     * 00:00:00 23:59:59
     */
    public static final String DEFAULT_TIME_RANGE = "00:00:00" + " " + "23:59:59";

    /**
     * 00:00:00
     */
    public static final String DEFAULT_START_TIME = "00:00:00";

    /**
     * 23:59:59
     */
    public static final String DEFAULT_END_TIME = "23:59:59";

    /**
     * 对于一个Date,对其指定单位的部分进行增加或者减少
     *
     * @param date      被调整的日期，null表示当前时间
     * @param fieldType {@link Calendar}类定义的各种常量，年、月、周、日、时、分、秒、毫秒
     * @param amout     调整值 正数表示增加，负数表示减少
     * @return
     */
    public static Date adjust(Date date, int fieldType, int amout) {
        Calendar cal = Calendar.getInstance();

        if (date != null) {
            cal.setTime(date);
        }

        cal.add(fieldType, amout);

        return cal.getTime();
    }

    /**
     * 获取系统当前时间距离1970年1月1日零点的秒数
     *
     * @return
     */
    public static long currentTimeSeconds() {
        return System.currentTimeMillis() / 1000;
    }

    /**
     * 按格式化字符串："yyyy-MM-dd HH:mm:ss" 格式化日期对象
     *
     * @param date
     * @return
     */
    public static String format(Date date) {
        return format(date, DEFAULT_DATE_TIME);
    }

    /**
     * 指定格式化字符串，格式化日期对象
     *
     * @param date
     * @param format <li>yyyy:年份
     *               <li>MM:月份
     *               <li>dd:日期
     *               <li>HH:小时
     *               <li>mm:分钟
     *               <li>ss:秒
     *               <li>ms:毫秒
     * @return
     */
    public static String format(Date date, String format) {
        if (date == null) {
            return "";
        }

        DateFormat dateFormat = getDateFormat(format);
        return dateFormat.format(date);
    }

    /**
     * 根据年和月拿到当月的天数
     *
     * @param year  年
     * @param month 月
     * @return
     */
    public static int getDaysInMonth(int year, int month) {
        Calendar cal = Calendar.getInstance();
        cal.set(year, month - 1, 1, 0, 0, 0);

        return cal.getActualMaximum(Calendar.DATE);
    }

    /**
     * 获取当前日期的日份
     */
    public static int getDayValue(Date d) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(d);
        return calendar.get(Calendar.DAY_OF_MONTH);
    }

    /**
     * 获取两个日期之间的相隔天数
     *
     * @param Date date1
     * @param Date date2
     * @return days date2 - date1 的天数
     */
    public static int getDifferDays(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        clearTimeField(cal1);

        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        clearTimeField(cal2);

        long betweenDays = (cal2.getTimeInMillis() - cal1.getTimeInMillis()) / (1000 * 3600 * 24);
        return (int) betweenDays;
    }

    /**
     * 获取两个日期之间的相隔月数
     *
     * @param Date date1
     * @param Date date2
     * @return months date2 - date1 的月数
     */
    public static int getDifferMonths(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);

        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);

        int year1 = cal1.get(Calendar.YEAR);
        int year2 = cal2.get(Calendar.YEAR);

        int month1 = cal1.get(Calendar.MONTH);
        int month2 = cal2.get(Calendar.MONTH);

        int betweenMonths = (year2 - year1) * 12 + (month2 - month1);

        return betweenMonths;
    }

    /**
     * 获取两个日期之间的相隔年数
     *
     * @param Date date1
     * @param Date date2
     * @return years date2 - date1 的年数
     */
    public static int getDifferYears(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);

        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);

        int betweenYears = (cal2.get(Calendar.YEAR) - cal1.get(Calendar.YEAR));
        return betweenYears;
    }

    /**
     * 获取当月第一天
     *
     * @return
     */
    public static Date getFirstDayOfMonth() {
        return getFirstDayOfMonth(null);
    }

    /**
     * 获取指定日期所在月的第一天
     *
     * @param date
     * @return
     */
    public static Date getFirstDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();

        if (date != null) {
            cal.setTime(date);
        }

        cal.set(Calendar.DAY_OF_MONTH, 1);
        clearTimeField(cal);

        return cal.getTime();
    }

    /**
     * 获取本周一
     *
     * @return
     */
    public static Date getFirstDayOfWeek() {
        return getFirstDayOfWeek(null);
    }

    /**
     * 获取指定日期所在周的周一
     *
     * @param date
     * @return
     */
    public static Date getFirstDayOfWeek(Date date) {
        Calendar cal = Calendar.getInstance();

        if (date != null) {
            cal.setTime(date);
        }

        cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        clearTimeField(cal);

        return cal.getTime();
    }

    /**
     * 获取当月最后一天
     *
     * @return
     */
    public static Date getLastDayOfMonth() {
        return getLastDayOfMonth(null);
    }

    /**
     * 获取指定日期所在月的最后一天
     *
     * @param date
     * @return
     */
    public static Date getLastDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();

        if (date != null) {
            cal.setTime(date);
        }

        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        clearTimeField(cal);

        return cal.getTime();
    }

    /**
     * 获取本周日
     *
     * @return
     */
    public static Date getLastDayOfWeek() {
        return getLastDayOfWeek(null);
    }

    /**
     * 获取指定日期所在周的周日
     *
     * @param date
     * @return
     */
    public static Date getLastDayOfWeek(Date date) {
        Calendar cal = Calendar.getInstance();

        if (date != null) {
            cal.setTime(date);
        }

        cal.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
        cal.add(Calendar.DATE, 1);
        clearTimeField(cal);

        return cal.getTime();
    }

    /**
     * 获取指定日期的月份
     *
     * @param date
     */
    public static int getMonthValue(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.MONTH) + 1;
    }

    /**
     * 获取当前日期是星期几<br>
     * 1 ＝ 星期日，2=星期一，3=星期二，4=星期三，5=星期四，6=星期五，7=星期六
     *
     * @param date
     * @return 当前日期是星期几
     */
    public static String getWeekOfDate(Date date) {
        String[] weekDays = {"1", "2", "3", "4", "5", "6", "7"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0) {
            w = 0;
        }
        return weekDays[w];
    }

    /**
     * 获取当前日期的年份
     */
    public static int getYearValue(Date d) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(d);
        return calendar.get(Calendar.YEAR);
    }

    public static boolean isDateClass(Class<?> cls) {
        return Date.class.isAssignableFrom(cls);
    }

    /**
     * 获取接近当天24点的时间点，即第二天的0点少1毫秒;
     *
     * @return
     */
    public static Date limitMidnight() {
        Date d = adjust(today(), Calendar.DATE, 1);
        return adjust(d, Calendar.MILLISECOND, -1);
    }

    /**
     * 获取接近指定日期的24点的时间点，即第二天的0点少1毫秒
     *
     * @param date
     * @return
     */
    public static Date limitMidnight(Date date) {
        Calendar cal = Calendar.getInstance();
        if (date != null) {
            cal.setTime(date);
        }
        clearTimeField(cal);

        Date d = adjust(cal.getTime(), Calendar.DATE, 1);
        return adjust(d, Calendar.MILLISECOND, -1);
    }

    /**
     * 获取当天24点的时间点，即第二天的0点;
     *
     * @return
     */
    public static Date midnight() {
        return adjust(today(), Calendar.DATE, 1);
    }

    /**
     * 获取指定日期的24点的时间点，即第二天的0点
     *
     * @param date
     * @return
     */
    public static Date midnight(Date date) {
        Calendar cal = Calendar.getInstance();
        if (date != null) {
            cal.setTime(date);
        }
        clearTimeField(cal);

        return adjust(cal.getTime(), Calendar.DATE, 1);
    }

    /**
     * 获取当天日期与时间
     *
     * @return
     */
    public static Date now() {
        Calendar cal = Calendar.getInstance();
        return cal.getTime();
    }

    /**
     * 转换日期,如果参数是String类型，按"yyyy-MM-dd HH:mm:ss"和"yyyy-MM-dd"转换
     *
     * @param val
     * @return
     * @throws ParseException
     * @see DateUtils#parse(String, String)
     */
    public static Date parse(Object val) {
        if (val == null) {
            return null;
        }

        if (val instanceof Timestamp) {
            return new Date(((Timestamp) val).getTime());
        } else if (val instanceof Date) {
            return (Date) val;
        } else if (val instanceof Integer || val instanceof Long) {
            return new Date(NumberUtils.parseLong(val));
        } else if (val instanceof java.sql.Date) {
            return new Date(((java.sql.Date) val).getTime());
        } else if (val instanceof Calendar) {
            return ((Calendar) val).getTime();
        } else if (val instanceof LocalDate) {
            return Date.from(((LocalDate) val).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
        } else if (val instanceof LocalDateTime) {
            return Date.from(((LocalDateTime) val).atZone(ZoneId.systemDefault()).toInstant());
        } else {
            Date date = tryParse(String.valueOf(val), DEFAULT_DATE_TIME);
            return date != null ? date : tryParse(String.valueOf(val), DEFAULT_DATE);
        }
    }

    /**
     * 转换日期
     *
     * @param val
     * @param format 如果参数是String类型，按此参数转换
     *               <li>yyyy:年份
     *               <li>MM:月份
     *               <li>dd:日期
     *               <li>HH:小时
     *               <li>mm:分钟
     *               <li>ss:秒
     *               <li>ms:毫秒
     * @return
     * @throws ParseException
     */
    public static Date parse(String val, String format) throws ParseException {
        DateFormat dateFormat = getDateFormat(format);
        return dateFormat.parse(val.toString());
    }

    /**
     * 对于一个Date设置时间部分的值
     *
     * @param date        设置的dated对象
     * @param hour        设置小时，null不理会
     * @param minute      设置分钟，null不理会
     * @param second      设置秒，null不理会
     * @param millisecond 设置毫秒，null不理会
     * @return
     */
    public static Date setTime(Date date, Integer hour, Integer minute, Integer second, Integer millisecond) {
        Calendar calendar = Calendar.getInstance();

        if (date != null) {
            calendar.setTime(date);
        }

        if (hour != null) {
            calendar.set(Calendar.HOUR_OF_DAY, hour);
        }

        if (minute != null) {
            calendar.set(Calendar.MINUTE, minute);
        }

        if (second != null) {
            calendar.set(Calendar.SECOND, second);
        }

        if (millisecond != null) {
            calendar.set(Calendar.MILLISECOND, millisecond);
        }

        return calendar.getTime();
    }

    /**
     * 获取当天日期，不包括时间部分
     *
     * @return
     */
    public static Date today() {
        Calendar cal = Calendar.getInstance();
        clearTimeField(cal);
        return cal.getTime();
    }

    /**
     * 将{@link Date}转换成{@link LocalDate}
     *
     * @param date
     * @return
     */
    public static LocalDate toLocalDate(Date date) {
        return toLocalDateTime(date).toLocalDate();
    }

    /**
     * 将{@link Date}转换成{@link LocalDateTime}
     *
     * @param date
     * @return
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        Assert.notNull(date, "paramter 'date' is required");

        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }

    public static Date tryParse(String val, String format) {
        try {
            return parse(val, format);
        } catch (Exception e) {
            return null;
        }
    }

    private static void clearTimeField(Calendar cal) {
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
    }

    private static DateFormat getDateFormat(String format) {
        return new SimpleDateFormat(format);
    }

}